package com.mlethe.library.fingerprint;

import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal;

import com.mlethe.library.fingerprint.callback.FingerprintCallback;
import com.mlethe.library.fingerprint.callback.IFingerprint;
import com.mlethe.library.fingerprint.entity.VerificationDialogStyle;
import com.mlethe.library.fingerprint.uitls.CipherHelper;
import com.mlethe.library.fingerprint.uitls.FingerprintUtil;

import javax.crypto.Cipher;

/**
 * Android M == 6.0
 * Created by Mlethe on 2019/7/9.
 */
@RequiresApi(api = Build.VERSION_CODES.M)
class FingerprintImplForNoDialog implements IFingerprint {

    /**
     * Android 6.0 指纹管理
     */
    private FingerprintManagerCompat fingerprintManagerCompat;

    /**
     * 指纹加密
     */
    private FingerprintManagerCompat.CryptoObject cryptoObject;

    /**
     * 用于取消扫描器的扫描动作
     */
    private CancellationSignal cancellationSignal;

    /**
     * 指向调用者的指纹回调
     */
    private FingerprintCallback fingerprintCallback;

    private static final class Holder {
        private static final FingerprintImplForNoDialog INSTANCE = new FingerprintImplForNoDialog();
    }

    private FingerprintImplForNoDialog() {
    }

    public static FingerprintImplForNoDialog getInstance() {
        return Holder.INSTANCE;
    }

    /**
     * 创建指纹加密
     * @return
     */
    @Override
    public boolean createCryptoObject(boolean isSetting) {
        // 指纹加密，提前进行Cipher初始化，防止指纹认证时还没有初始化完成
        try {
            if (cryptoObject == null) {
                CipherHelper cipherHelper = CipherHelper.getInstance();
                Cipher cipher = cipherHelper.createCipher();
                if (isSetting) {
                    cipherHelper.createKey();
                }
                if (cipherHelper.initCipher(cipher)) {
                    return true;
                }
                cryptoObject = new FingerprintManagerCompat.CryptoObject(cipher);
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 设置回调监听
     *
     * @param callback
     */
    @Override
    public void setFingerprintCallback(FingerprintCallback callback) {
        fingerprintCallback = callback;
    }

    @Override
    public void setVerificationDialogStyle(VerificationDialogStyle verificationDialogStyle) {
    }

    /**
     * 调起指纹验证
     *
     * @param context
     */
    @Override
    public void authenticate(@NonNull Context context) {
        Context appContext = context.getApplicationContext();
        // Android 6.0 指纹管理 实例化
        if (fingerprintManagerCompat == null) {
            fingerprintManagerCompat = FingerprintManagerCompat.from(appContext);
        }
        // 取消扫描，每次取消后需要重新创建新示例
        cancellationSignal = new CancellationSignal();
        // 认证结果回调
        FingerprintManagerCompat.AuthenticationCallback authenticationCallback = new FingerprintManagerCompat.AuthenticationCallback() {
            @Override
            public void onAuthenticationError(int errorCode, CharSequence errString) {
                super.onAuthenticationError(errorCode, errString);
                if (errorCode == FingerprintManager.FINGERPRINT_ERROR_HW_NOT_PRESENT ||
                        errorCode == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
                    if (fingerprintCallback != null) {
                        fingerprintCallback.onHwUnavailable();
                    }
                } else if (errorCode == FingerprintManager.FINGERPRINT_ERROR_NO_FINGERPRINTS) {
                    if (fingerprintCallback != null) {
                        fingerprintCallback.onNoneEnrolled();
                    }
                } else if (errorCode == FingerprintManager.FINGERPRINT_ERROR_CANCELED ||
                        errorCode == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED) {
                    // 用户取消指纹验证
                    if (fingerprintCallback != null) {
                        fingerprintCallback.onCancel();
                    }
                } else {
                    if (fingerprintCallback != null) {
                        fingerprintCallback.onError(errString.toString());
                    }
                }
                onDestroy();
            }

            @Override
            public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
                super.onAuthenticationHelp(helpMsgId, helpString);
                if (fingerprintCallback != null) {
                    fingerprintCallback.onFailed(helpString.toString());
                }
            }

            @Override
            public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
                super.onAuthenticationSucceeded(result);
                if (fingerprintCallback != null) {
                    fingerprintCallback.onSucceeded();
                }
                onDestroy();
            }

            @Override
            public void onAuthenticationFailed() {
                super.onAuthenticationFailed();
                if (fingerprintCallback != null) {
                    fingerprintCallback.onFailed("验证失败");
                }
            }
        };
        // 调起指纹验证
        fingerprintManagerCompat.authenticate(cryptoObject, 0, cancellationSignal, authenticationCallback, null);
    }

    /**
     * 取消指纹识别
     */
    @Override
    public void cancel() {
        if (cancellationSignal != null && !cancellationSignal.isCanceled()) {
            cancellationSignal.cancel();
        }
    }

    /**
     * 在 Android Q，Google 提供了 Api BiometricManager.canAuthenticate() 用来检测指纹识别硬件是否可用及是否添加指纹
     * 不过尚未开放，标记为"Stub"(存根)
     * 所以暂时还是需要使用 Andorid 6.0 的 Api 进行判断
     */
    @Override
    public boolean canAuthenticate(Context context) {
        /*
         * 硬件是否支持指纹识别
         */
        if (!FingerprintUtil.isHardwareDetected(context)) {
            if (fingerprintCallback != null) {
                fingerprintCallback.onHwUnavailable();
            }
            onDestroy();
            return false;
        }
        /*
         * 是否已添加指纹
         */
        if (!FingerprintUtil.hasEnrolledFingerprints(context)) {
            if (fingerprintCallback != null) {
                fingerprintCallback.onNoneEnrolled();
            }
            onDestroy();
            return false;
        }
        return true;
    }

    @Override
    public void onDestroy() {
        cryptoObject = null;
        cancellationSignal = null;
        fingerprintCallback = null;
    }
}
